package
{
	import flash.utils.getTimer;
	
	public class CPU
	{
		/***** Limits *****/
		public static var tMax:uint = 10; /* Maximum length in msec of a macro cycle */
		public static var tLim:uint = 16; /* Absolute maximim safe macro cycle length */
		
		/***** Timing Instrumentation *****/
		private static var mc:uint = 0; /* Macro instruction count since last reset */
		private static var uc:uint = 0; /* Micro instruction count since last reset */
		private static var oc:uint = 0; /* Macro overtime count since last reset */
		private static var stime:uint = 0; /* Start Time */
		
		/***** CPU State *****/
		/* Timing */
		private static var tstart:uint; /* time at start of macrocycle */
		private static var tend:uint; /* time when macrocycle must end */
		private static var umore:Boolean; /* whether to advance to next microcycle */
		/* registers...? */
		private static var IP:uint = 0; /* Instruction Pointer */
		private static var SW:uint = 0; /* CPU Status Word */
		
		/* Internal */
		private static var EXEC:uint;
		
		/* stack...? */
		/* scratchpad...? */
		
		/******************/
		
		/* Idea: Should threading be a CPU primitive? */
		
		/* Halt instruction: stop CPU clock entirely;
		   Wake (on I/O): restart and immediately start a macrocycle */

		/* macro cycle - these should always fit in tMax, if possible */
		public static function cycle():void {
			mc++; /* Up macro instruction count */
			/* getTimer() takes time. after we find out how long things take, 
			   we can start estimating ucycle times, and stop calling it. */
			tstart = getTimer();
			umore = true; /* Whether to advance to next ucycle */
			do { /* micro cycle - these may vary in duration */
				/****** BEGIN INSTRUCTION CYCLE ******/
				uc++; /* Up micro instruction count */
				if (IRQ.IF) { /* check for interrupt flag */
					IRQ.IF = false;
					if (IRQ.device==IRQ.MouseIRQ && IRQ.signal==IRQ.MouseDown) Test.drawchar(0+IRQ.va,0+IRQ.vb); /* test */
					/* look up interrupt vector */
					
				}
				
				/****** FETCH ******/
				
				//EXEC = RAM.ReadW(IP);
				
				/****** DECODE ******/
				
				/* First, check if this word is an 'invisible pointer.' */
				
				
				/* break loop on 'yield' - ends macrocycle */
				/* TODO: up overtime counter if necessary */
				
				
				/* ucycle completed */
				tend = getTimer();
				if (tend > tstart + tMax) umore = false; /* End of macrocycle */
				if (tend > tstart + tLim) oc++; /* Overtime! */
				/* Should we automatically detect excess missed macrocycles? */
				/******* END INSTRUCTION CYCLE *******/
			} while (umore);
		}
		
		/* Cold Start */
		public static function init():void {
			reset();
			m1.SysClk.off();
			m1.SysClk.hook(cycle);
			m1.SysClk.on();
			stime = getTimer();
		}
		
		/* Halt CPU */
		public static function halt():void {
			m1.SysClk.off();
		}
		
		/* Cold Reset */
		public static function reset():void {
			/* Wipe all registers */
			IP = 0;
			mc = 0;
			uc = 0;
			oc = 0;
		}
		
		public static function dump():void {
			debug("Macro-cycles="+mc);
			debug("Micro-cycles="+uc);
			debug("Overtimes="+oc);
			debug("Micro-cycles/sec="+ 1000*uc/(getTimer() - stime));
		}

        private static function debug(msg:String):void {
        	m1.debug.print("CPU: " + msg);
        }

	}
}